//  Spea2Fitness.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package jmetal.util;

import jmetal.core.Solution;
import jmetal.core.SolutionSet;
import jmetal.util.comparators.DominanceComparator;
import jmetal.util.comparators.FitnessComparator;
import jmetal.util.comparators.MinAngleComparator;
import jmetal.util.comparators.MinDistanceComparator;
import jmetal.util.comparators.OccupyVectorsComparator;
import jmetal.util.comparators.nearestComparator;
import jmetal.util.dir.DIRCompute;

import java.util.*;

/**
 * This class implements some facilities for calculating the Spea2 fitness
 */
public class ASpea2Fitness {
  
  /**
   * Stores the solutionSet to assign the fitness
   */
  private SolutionSet solutionSet_ = null;
  
  /** 
   * stores a <code>Distance</code> object
   */
  private static final Distance distance_ = new Distance();
  
  /**
   * stores a <code>Comparator</code> for distance between nodes checking
   */
  private static final Comparator distanceNodeComparator = new DistanceNodeComparator();      
  
  /**
   * stores a <code>Comparator</code> for dominance checking
   */
  private static final Comparator dominance_ = new DominanceComparator();
  
  /** 
   * Constructor.
   * Creates a new instance of Spea2Fitness for a given <code>SolutionSet</code>.
   * @param solutionSet The <code>SolutionSet</code>
   */
  public ASpea2Fitness(SolutionSet solutionSet) {     
    solutionSet_ = solutionSet;
    for (int i = 0; i < solutionSet_.size(); i++) {
      solutionSet_.get(i).setLocation(i);
    } // for
  } // Spea2Fitness
    
    
  /** 
   * Assigns fitness for all the solutions.
   */
  public void fitnessAssign(int vectorNum) {
    double [] strength    = new double[solutionSet_.size()];
    double [] rawFitness  = new double[solutionSet_.size()];                                     ;
  
    
    //Calculate the strength value
    // strength(i) = |{j | j <- SolutionSet and i dominate j}|
    for (int i = 0; i < solutionSet_.size(); i++) {
      for (int j = 0; j < solutionSet_.size();j++) {
        if (dominance_.compare(solutionSet_.get(i),solutionSet_.get(j))==-1) {
          strength[i] += 1.0;
        } // if        
      } // for
    } // for
       
        
    //Calculate the raw fitness
    // rawFitness(i) = |{sum strenght(j) | j <- SolutionSet and j dominate i}|
    for (int i = 0;i < solutionSet_.size(); i++) {
      for (int j = 0; j < solutionSet_.size();j++) {
        if (dominance_.compare(solutionSet_.get(i),solutionSet_.get(j))==1) {
          rawFitness[i] += strength[j];
        } // if
      } // for
    } // for
       
        

    //set the number of occupy vectors for solutions 
    new DIRCompute().assessDir(solutionSet_, vectorNum);
    for(int i = 0;i<solutionSet_.size();i++){
    	double inverCount = 1.0/(double)(solutionSet_.get(i).getCountOfOccupyVectors()+2);
    	solutionSet_.get(i).setFitness(inverCount+rawFitness[i]);
    }              
  } // fitnessAsign
    
    
  /** 
  *  Gets 'size' elements from a population of more than 'size' elements
  *  using for this de enviromentalSelection truncation
  *  @param size The number of elements to get.
  */
  public SolutionSet environmentalSelection(int size){        
    
    if (solutionSet_.size() < size) {
      size = solutionSet_.size();
    }
        
    // Create a new auxiliar population for no alter the original population
    SolutionSet aux = new SolutionSet(solutionSet_.size());
                
    int i = 0;
    while (i < solutionSet_.size()){
      if (solutionSet_.get(i).getFitness()<1.0){
        aux.add(solutionSet_.get(i));
        solutionSet_.remove(i);
      } else {
        i++;
      } // if
    } // while
                
    if (aux.size() < size){
      Comparator comparator = new FitnessComparator();
      solutionSet_.sort(comparator);
      int remain = size - aux.size();
      for (i = 0; i < remain; i++){
        aux.add(solutionSet_.get(i));
      }
      return aux;
    } else if (aux.size() == size) {
      return aux;            
    }        
    

	while (aux.size() > size) {
		new DIRCompute().assessDir(aux, size);
		Comparator comparator = new OccupyVectorsComparator();
		aux.sort(comparator);
		for (int loc = 0; loc < aux.size(); loc++)
			aux.get(loc).setLocation(loc);

		Solution solution = aux.get(0);
		SolutionSet worstSet = new SolutionSet(size * 2);
		int pos = 1;
		while (pos < aux.size() && solution.getCountOfOccupyVectors() == aux.get(pos).getCountOfOccupyVectors()) {
			worstSet.add(aux.get(pos));
			pos++;
		}
		
		if (worstSet.size() != 0) {
			if (worstSet.get(0).getCountOfOccupyVectors() == 0) {
				new DIRCompute().assessNearest(worstSet, aux);
				Comparator nearestComparator = new nearestComparator();
				worstSet.sort(nearestComparator);
				aux.remove(worstSet.get(worstSet.size()-1).getLocation());
			} else {//可以修改
				Comparator distanceComparator = new MinDistanceComparator();
				worstSet.sort(distanceComparator);
				aux.remove(worstSet.get(worstSet.size() - 1).getLocation());
			}
		} else
			aux.remove(0);

	}
    
    return aux;
  } // environmentalSelection   
} // Spea2Fitness
